package loginserver;

/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * Credit goes to l2j-client project <https://code.google.com/p/l2jserver-client/> and l2j project <http://www.l2jserver.com/>
 */

import java.util.logging.Logger;

/**
 * Abstract base class for login packets
 * 
 */
// TODO check for refactoring and consolidation with GameServerPacket
public class LoginServerPacket
{

	protected static Logger log = Logger.getLogger(LoginServerPacket.class.getName());

	private byte[] _decrypt;
	private int _off;

	/**
	 * Reads 32 bits
	 * 
	 * @return Int value representing the 32 bits
	 */
	public int readD()
	{
		int result = _decrypt[_off++] & 0xff;
		result |= _decrypt[_off++] << 8 & 0xff00;
		result |= _decrypt[_off++] << 0x10 & 0xff0000;
		result |= _decrypt[_off++] << 0x18 & 0xff000000;
		return result;
	}

	/**
	 * Reads a byte
	 * 
	 * @return Int value returned from the read byte
	 */
	public int readC()
	{
		int result = _decrypt[_off++] & 0xff;
		return result;
	}

	/**
	 * Reads 16 bits from the buffer
	 * 
	 * @return Int value returned from the read byte
	 */
	public int readH()
	{
		int result = _decrypt[_off++] & 0xff;
		result |= _decrypt[_off++] << 8 & 0xff00;
		return result;
	}

	/**
	 * Reads 64 bits
	 * 
	 * @return Double representing the 64 bit value
	 */
	public double readF()
	{
		long result = (((long) (_decrypt[_off++] & 0xff)) | ((long) (_decrypt[_off++] & 0xff) << 8) | ((long) (_decrypt[_off++] & 0xff) << 16) | ((long) (_decrypt[_off++] & 0xff) << 24) | ((long) (_decrypt[_off++] & 0xff) << 32) | ((long) (_decrypt[_off++] & 0xff) << 40) | ((long) (_decrypt[_off++] & 0xff) << 48) | ((long) (_decrypt[_off++] & 0xff) << 56));
		return Double.longBitsToDouble(result);

	}

	/**
	 * Reads 64 bits
	 * 
	 * @return long representing the 64 bit value
	 */
	public long readQ()
	{
		long result = (((long) (_decrypt[_off++] & 0xff)) | ((long) (_decrypt[_off++] & 0xff) << 8) | ((long) (_decrypt[_off++] & 0xff) << 16) | ((long) (_decrypt[_off++] & 0xff) << 24) | ((long) (_decrypt[_off++] & 0xff) << 32) | ((long) (_decrypt[_off++] & 0xff) << 40) | ((long) (_decrypt[_off++] & 0xff) << 48) | ((long) (_decrypt[_off++] & 0xff) << 56));
		return result;
	}

	/**
	 * Reads a UTF-16LE String from the byte array (until 0x00 is read) Conversionexceptions are swallowed
	 * 
	 * @return String value read, or null
	 */
	public String readS()
	{
		String result = null;
		try
		{
			result = new String(_decrypt, _off, _decrypt.length - _off, "UTF-16LE");
			result = result.substring(0, result.indexOf(0x00));
		}
		catch (Exception e)
		{
			e.printStackTrace();
			_off += 2;
			return "";
		}
		_off += result.length() * 2 + 2;
		return result;

	}

	/**
	 * Reads raw bytes in
	 * 
	 * @param length
	 *            # of bytes to be returned
	 * @return a new byte array containing the read bytes
	 */
	public final byte[] readB(int length)
	{
		byte[] result = new byte[length];
		for (int i = 0; i < length; i++)
		{
			result[i] = _decrypt[_off + i];
		}
		_off += length;
		return result;
	}

	/**
	 * Sets the internal buffer to the passed bytes and the read offset to 3 (skipping the first two id bytes).
	 * 
	 * @param raw
	 *            byte array buffer to be stored
	 */
	public void setBytes(byte[] raw)
	{
		if (raw != null && raw.length > 2)
		{
			_decrypt = raw;
			_off = 3;
		}
	}
	
	public byte[] getBytes()
	{
		return _decrypt;
	}

	/**
	 * writes 32 bits
	 */
	public void writeD(int value)
	{
		_decrypt[_off++] = (byte) (value & 0xff);
		_decrypt[_off++] = (byte) (value >> 8 & 0xff);
		_decrypt[_off++] = (byte) (value >> 16 & 0xff);
		_decrypt[_off++] = (byte) (value >> 24 & 0xff);
	}

	/**
	 * writes a byte
	 */
	public void writeC(int value)
	{
		_decrypt[_off++] = (byte) (value & 0xff);
	}

	/**
	 * writes 16 bits to the buffer
	 */
	public void writeH(int value)
	{
		_decrypt[_off++] = (byte) (value & 0xff);
		_decrypt[_off++] = (byte) (value >> 8 & 0xff);
	}

	/**
	 * writes 64 bits
	 */
	public void writeF(double org)
	{
		long value = Double.doubleToRawLongBits(org);
		_decrypt[_off++] = (byte) (value & 0xff);
		_decrypt[_off++] = (byte) (value >> 8 & 0xff);
		_decrypt[_off++] = (byte) (value >> 16 & 0xff);
		_decrypt[_off++] = (byte) (value >> 24 & 0xff);
		_decrypt[_off++] = (byte) (value >> 32 & 0xff);
		_decrypt[_off++] = (byte) (value >> 40 & 0xff);
		_decrypt[_off++] = (byte) (value >> 48 & 0xff);
		_decrypt[_off++] = (byte) (value >> 56 & 0xff);

	}

	/**
	 * writes 64 bits It should work, but I am not 100% sure
	 */
	@Deprecated
	public void writeQ(int value)
	{
		_decrypt[_off++] = (byte) (value & 0xff);
		_decrypt[_off++] = (byte) (value >> 8 & 0xff);
		_decrypt[_off++] = (byte) (value >> 16 & 0xff);
		_decrypt[_off++] = (byte) (value >> 24 & 0xff);
		_decrypt[_off++] = (byte) (value >> 32 & 0xff);
		_decrypt[_off++] = (byte) (value >> 40 & 0xff);
		_decrypt[_off++] = (byte) (value >> 48 & 0xff);
		_decrypt[_off++] = (byte) (value >> 56 & 0xff);
	}

	/**
	 * writes a UTF-16LE String to the byte array (until 0x00 is read) Conversionexceptions are swallowed
	 */
	public void writeS(String text)
	{
		byte[] bytes;
		try
		{
			bytes = text.getBytes("UTF-16LE");
			for (byte b : bytes)
			{
				_decrypt[_off++] = b;
			}
			_decrypt[_off++] = 0x00;
			_decrypt[_off++] = 0x00;
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Reads raw bytes in
	 * 
	 * @param length
	 *            # of bytes to be returned
	 * @return a new byte array containing the read bytes
	 */
	public final void writeB(byte[] value)
	{
		for (byte b : value)
			_decrypt[_off++] = b;
	}

}
